package ar.fiuba.logger.configfile;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import ar.fiuba.logger.LogConfig;
import ar.fiuba.logger.LogConfigReader;
import ar.fiuba.logger.LogFormatterInterface;
import ar.fiuba.logger.LogLevel;
import ar.fiuba.logger.filter.LogLevelLessThanOrEqualToThresholdFilter;
import ar.fiuba.logger.filter.MessageMatchesRegularExpressionFilter;
import ar.fiuba.logger.formatter.LogTextFormatter;
import ar.fiuba.logger.reporter.ConsoleReporter;
import ar.fiuba.logger.reporter.FileReporter;

/**
 * The Class LogConfigFile.
 * 
 * This class implements the log configuration file. It parses configuration details from a properties files.
 */
public class LogConfigPropertiesFile implements LogConfigReader {

  /** The filename. */
  protected String filename;

  /**
   * Instantiates a new log configuration file.
   * 
   * @param filename
   *            the filename
   */
  public LogConfigPropertiesFile(String filename) {
    this.filename = filename;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    LogConfigPropertiesFile other = (LogConfigPropertiesFile) obj;
    if (filename == null) {
      if (other.filename != null)
        return false;
    } else if (!filename.equals(other.filename))
      return false;
    return true;
  }

  /**
   * Gets the default properties.
   * 
   * @return the default properties
   */
  private Properties getDefaultProperties() {
    LogConfig logConfig = new LogConfig();
    Properties defaultProperties = new Properties();

    defaultProperties.setProperty("separator", logConfig.separator);
    defaultProperties.setProperty("format", logConfig.format);
    defaultProperties.setProperty("log_level", logConfig.threshold.name());
    defaultProperties.setProperty("console", "ON");
    defaultProperties.setProperty("log_file", "");
    defaultProperties.setProperty("regex", "");

    return defaultProperties;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((filename == null) ? 0 : filename.hashCode());
    return result;
  }

  /**
   * Parses the config.
   * 
   * @param prop
   *            the prop
   * @return the log config
   * @throws FileNotFoundException
   *             the file not found exception
   * @throws UnsupportedEncodingException
   *             the unsupported encoding exception
   */
  private LogConfig parseConfig(Properties prop) throws FileNotFoundException,
      UnsupportedEncodingException {
    List<String> files = new ArrayList<String>();
    LogConfig logConfig = new LogConfig();

    logConfig.reporters.clear();
    logConfig.filters.clear();

    logConfig.separator = prop.getProperty("separator");
    logConfig.format = prop.getProperty("format");
    logConfig.threshold = LogLevel.valueOf(prop.getProperty("log_level").toUpperCase());
    logConfig.regex = prop.getProperty("regex");

    logConfig.filters.add(new LogLevelLessThanOrEqualToThresholdFilter(logConfig.threshold));

    /*
     * For the moment all FileReporters and the ConsoleReporter use the same format configuration,
     * this may be changed in the future.
     */
    LogFormatterInterface formatter = new LogTextFormatter(logConfig.format, logConfig.separator);

    if (prop.getProperty("console").compareToIgnoreCase("ON") == 0) {
      logConfig.reporters.add(new ConsoleReporter(formatter));
    }

    if (!prop.getProperty("log_file").isEmpty()) {
      files = Arrays.asList(prop.getProperty("log_file").split(","));

      for (int i = 0; i < files.size(); i++) {
        logConfig.reporters.add(new FileReporter(files.get(i), formatter));
      }
    }

    if (!logConfig.regex.isEmpty()) {
      logConfig.filters.add(new MessageMatchesRegularExpressionFilter(logConfig.regex));
    }

    return logConfig;
  }

  /**
   * Read.
   * 
   * @return the log configuration
   */
  public LogConfig read() {
    Properties prop = new Properties(this.getDefaultProperties());
    InputStream input = null;
    LogConfig config = new LogConfig();

    try {
      input = new FileInputStream(filename);

      prop.load(input);

      config = this.parseConfig(prop);
    } catch (IOException ex) {
      System.err.print("An error has ocurred while trying to read the config file.");
      ex.printStackTrace();

      /*
       * In case that an error occurs while reading the config file, the default config is used instead.
       */
      config = new LogConfig();
    } finally {
      if (input != null) {
        try {
          input.close();
        } catch (IOException e) {
          System.err.print("An error has ocurred while closing the config file.");
          e.printStackTrace();
        }
      }
    }

    return config;
  }
}
